﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;

namespace Flit.Samples.GivenWhenThen
{
    partial class Story
    {
        public void Execute()
        {
            ExecutionVisitor v = new ExecutionVisitor(Console.Out);
            v.Visit(this);
            foreach (FluentStateBase state in Descendants())
            {
                state.VisitCallback(v);
            }
            Exception exception = v._exceptions.FirstOrDefault();
            if (exception != null)
            {
                throw new AssertionException("Story failed", exception);
            }
        }

    }

    public class ExecutionVisitor : IFragmentVisitor
    {
        private readonly TextWriter _writer;
        internal readonly List<Exception> _exceptions = new List<Exception>();

        public ExecutionVisitor(TextWriter writer)
        {
            _writer = writer;
        }

        public void Visit(Story fragment)
        {
            _writer.WriteLine("Story is: " + fragment.Title);
        }

        public void Visit(Condition fragment)
        {
            write(1, "given", fragment);
        }

        public void Visit(Operation fragment)
        {
            write(2, "when", fragment);
        }

        public void Visit(Outcome fragment)
        {
            write(3, "then", fragment);
        }

        public void write(int indent, string name, IStoryFragment fragment)
        {
            StringBuilder sb = new StringBuilder();
            
            for (int i = 0; i < indent; i++)
            {
                sb.Append(' ');
            }
            if (fragment.IsAnd)
            {
                sb.Append("and");
            }
            else
            {
                sb.Append(name);
            }
            sb.Append(' ');
            sb.Append(fragment.Text);
            sb.Append(":");

            while(sb.Length < 40)
            {
                sb.Append(' ');
            }

            try
            {
                fragment.Action();
                sb.Append("passed :)");
            }
            catch (Exception e)
            {
                sb.Append("failed! " + e.Message);
                _exceptions.Add(e);
            }
            _writer.WriteLine(sb.ToString());
        }
    }


    public interface IStoryFragment
    {
        bool IsAnd { get; }
        string Text { get; }
        Action Action { get;  }
    }

    partial class Condition:IStoryFragment
    {
        public bool IsAnd { get; set; }

        partial void OnAnd(string text, Action setup)
        {
            Children.OfType<Condition>().Last().IsAnd = true;
        }
    }

    partial class Operation : IStoryFragment
    {
        public bool IsAnd { get; set; }

        partial void OnAnd(string text, Action setup)
        {
            Children.OfType<Operation>().Last().IsAnd = true;
        }
    }

    partial class Outcome : IStoryFragment
    {
        public bool IsAnd { get; set; }

        partial void OnAnd(string text, Action setup)
        {
            Children.OfType<Outcome>().Last().IsAnd = true;
        }
    }
}
